import sys

class Report(object):
          
    violations = []
    
    ignored_rules = []
    
    included_rules = []
    
    ignored_violations = []
    
    validated_path =""

    def add_rule_violation(self, violation):
        """
        Add a rule violation to the current report
        """
        if(violation == None):
            raise Exception("Null Rule reference added to a RuleViolation; that's a bug!")
        self.violations.append(violation)
        
    def remove_rule_violation(self, violation):
        """
        Remove a rule violation to the current report
        """
        if(violation == None):
            raise Exception("Null Rule reference removed to a RuleViolation; that's a bug!")
        self.violations.remove(violation)        
                
    def get_violations_by_severity(self, severity):
        """
        Return a list of violations that have a rule with the same severity as the value given
        """
        violations = []
        for violation in self.violations:
            if(violation.rule.priority == severity):
                violations.append(violation)
        return violations
    
    def get_violations_by_rule(self, rulename):
        """
        Return a list of violations that have a rule with the same name as the value given
        """
        violations = []
        for violation in self.violations:
            if(violation.rule.name == rulename):
                violations.append(violation)
        return violations
    
    def print_violations(self, mode=None):
        """
        Print all the violations. [Severity]: TestCase[Line] Error Message - Cause | Suggested solution
        """
        if("sorted" in mode):
            self.sort_violations()
        severity = self.get_severity(mode)
        for violation in self.violations:
            if(violation.rule.severity in severity):
                print violation
                              
    def get_severity(self, mode):
        for m in mode:
            if("severity" in m):
                severity = []
                values = m.split("=")[1].split(",")
                if("Error" in values): severity.append(0)
                if("Warning" in values): severity.append(1)
                if("Suggestion" in values): severity.append(2)
                return severity                    
        return [0, 1, 2]
                
    def sort_violations(self):
        """
        Sort the violation by rule name
        """        
        n = len(self.violations)
        for i in range(n):
            for j in range(i + 1, n):
                e1 = self.violations[i]
                e2 = self.violations[j]
                if(e1.rule.name > e2.rule.name):
                    self.violations[i] = e2
                    self.violations[j] = e1

    def __iter__(self):
        """
        Report iterator
        """
        return self.violations.__iter__()
    
    def export(self, path, exporter=None):
        """
        Write violations to a file
        """
        if(exporter):
            exporter.export_violations(self.violations, path)
        else:
            print "unable to export, exporter class not defined"
    
class ReportManager(object):
    """
    Report Manager is a Singleton class with the purpose of give at the entire system access 
    to a common Report instance
    """
    
    report = None
    instance = None
    
    def __new__(cls, *args, **kwargs):
        """
        Avoids the creation of a new instance. Always use the same one
        """
        if not cls.instance:
            cls.instance = super(ReportManager, cls).__new__(
                                cls, *args, **kwargs)
        return cls.instance
    
    def new_report(self, callback=None, ignore=None, include=None):
        """
        Create a new instance of Report, and replace the current one with this.
        """
        self.callback = callback
        if(self.instance.report != None):
            del self.instance.report
        self.instance.report = Report()
        self.instance.report.ignored_rules = ignore
        self.instance.report.included_rules = include
        self.instance.report.violations = []
        self._callback(state="reset")

    def _callback(self, state=None, **kwargs):
        '''Call the callback, if there is one

        state will be either "reset" or "violation". If state 
        is "violation", the keyword "violation" will contain the
        actual violation object.
        '''
        if self.callback is not None:
            try:
                self.callback(state=state, **kwargs)
            except Exception, e:
                # I'm not sure writing to stderr is the
                # right thing, but what else should I do?
                sys.stderr.write("unable to call callback: %s\n" % str(e))
            
    def add_rule_violation(self, violation):
        """
        Add a rule violation to the current report
        """
        if(self.instance.report != None):
            self.instance.report.add_rule_violation(violation)
        else:
            print "Error: Null Report!"

        self._callback(state="violation", violation=violation)
